/*
* Change Logs:
* Date           Author       Notes
* 2020-10-16     chenbin      mqtt client base on paho
* 2021-03-15     chenbin      mqtt client add rtthread port
*/
#ifndef __SMALL_MQTT_CLIENT_H__
#define __SMALL_MQTT_CLIENT_H__

#include <stdint.h>
#include <string.h>
#include <stdint.h>
#include <rtthread.h>
#include "MQTTPacket.h"

#define MQTT_SW_VERSION         "1.1.0"
#define MQTT_SW_VERSION_NUM     0x10100
#define MAX_MESSAGE_HANDLERS    2
#define MAX_PACKET_ID           65535
#define MQTT_SOCKET_TIMEO       6000

enum QoS { QOS0, QOS1, QOS2 };// ALIGN(4);

/* all failure return codes must be negative */
//enum returnCode { PAHO_BUFFER_OVERFLOW = -2, PAHO_FAILURE = -1, PAHO_SUCCESS = 0 };
//enum returnCode
//{
//    MQTT_RESTART = -2,
//    MQTT_FAIL = -1,
//    MQTT_OK = 0
//};

enum statusCode
{
    MQTT_NET_FAIL    = -6,
    MQTT_RESTART    = -3,
    MQTT_TIMEOUT    = -2,
    MQTT_FAIL       = -1,
    MQTT_OK         =  0,
    MQTT_CONNECT    = CONNECT,
    MQTT_CONNACK    = CONNACK,
    MQTT_PUBLISH    = PUBLISH,
    MQTT_PUBACK     = PUBACK,
    MQTT_PUBREC     = PUBREC,
    MQTT_PUBREL     = PUBREL,
    MQTT_PUBCOMP    = PUBCOMP,
    MQTT_SUBSCRIBE  = SUBSCRIBE,
    MQTT_SUBACK     = SUBACK,
    MQTT_UNSUBSCRIBE    = UNSUBSCRIBE,
    MQTT_UNSUBACK       = UNSUBACK,
    MQTT_PINGREQ        = PINGREQ,
    MQTT_PINGRESP       = PINGRESP,
    MQTT_DISCONNECT     = DISCONNECT
};

enum runCode
{
    MQTT_RUN_EXIT = 0,
    MQTT_RUN_INIT = 1,
    MQTT_RUN_CON ,
    MQTT_RUN_SUB ,
    MQTT_RUN_IDLE,
    MQTT_RUN_DIS
};

enum netCode
{
    MQTT_NET_INIT = 0,
    MQTT_NET_DIS  = 0,
    MQTT_NET_CON  = 1
};

enum mqttControl
{
	MQTT_CLIENT_INIT = 0,
	MQTT_SET_URI  = 0x01,
	MQTT_SET_HOST = 0x02,
	MQTT_SET_PORT = 0x03,
	
	MQTT_SET_READ_TIMEOUT_MS = 0x20,
	MQTT_SET_READ_BUFF_SIZE = 0x21,
	MQTT_SET_READ_BUFF = 0x22,
	
	MQTT_SET_WRITE_TIMEOUT_MS = 0x23,
	MQTT_SET_WRITE_BUFF_SIZE = 0x24,
	MQTT_SET_WRITE_BUFF = 0x25,
	
	MQTT_SET_CLIENTID = 0x30,
	MQTT_SET_USERNAME = 0x31,
	MQTT_SET_PASSWORD = 0x32,
	MQTT_SET_KEEPALIVE = 0x33,
	MQTT_SET_CLEANSESSION = 0x34,
	MQTT_SET_RECONNECT = 0x35,
	
	MQTT_CB_STATUS = 0x40,
	MQTT_CB_MESSAGE = 0x41,
};

enum mqttPort
{
	MQTT_PORT_NONE = 0,
	MQTT_PORT_DEVICE = 0x01,
	MQTT_PORT_SOCKET = 0x02,
};

typedef struct
{
  enum QoS qos;
  unsigned char retained;
  unsigned char dup;
  unsigned short id;
  void *payload;
  size_t payloadlen;
  MQTTString topic;
} mqtt_message_t;

typedef struct _mqtt_client mqtt_client_t;
typedef struct _mqtt_client_port mqtt_client_port_t;

/* subscribe topic receive data callback */
typedef void (*mqtt_status_cb)(mqtt_client_t *client, int status);
typedef void (*mqtt_message_cb)(mqtt_client_t *client, mqtt_message_t *msg);

struct _mqtt_client
{
	const char *chost;  //server url string
	const char *cport;  //port string
	int   socket_fd;		//socket fd
	void *socket_data;		//socket data
	void *write_lock;
	
	mqtt_client_port_t *port;
	
	uint8_t status_run;  //run status
	uint8_t status_net;  //network status
  uint16_t _next_packetid;
	
  uint32_t reconnect_interval;  //ms
  uint32_t read_timeout;    		//ms
  uint32_t write_timeout;   		//ms
  size_t read_size;         //byte
  size_t write_size;         //byte
  uint8_t *read_buff;
  uint8_t *write_buff;
  uint32_t _ping_keepalive;  //s
  uint32_t _ping_tick;
  uint32_t _ping_count;
  uint8_t _sub_index;
  uint8_t _sub_max;
  uint8_t _sub_used;
  uint8_t _sub_free;
  uint32_t _count_reconnect;
  uint32_t _count_ping;
  uint32_t _count_pingresp;
  uint32_t _count_server_publish;
  uint32_t _count_client_publish;
  uint32_t _count_client_publish_ack;
	
  MQTTPacket_connectData condata;
	
	mqtt_status_cb		status_handle;
  mqtt_message_cb 	message_handle_default;
	
  struct message_handlers
  {
    char *topicName;
    mqtt_message_cb callback;
    enum QoS qos;
  } message_handle[MAX_MESSAGE_HANDLERS];
};

/*
*mqtt clietn port
 */
struct _mqtt_client_port
{
	const uint32_t magic;
	int (*net_connect)(mqtt_client_t *client);
	int (*net_disconnect)(mqtt_client_t *client);
	int (*net_recv)(mqtt_client_t *client, unsigned char *buf, int len, int timeout_ms);
	int (*net_send)(mqtt_client_t *client, unsigned char *buf, int len);
};

#include "config_small_mqtt_client.h"
//#define MQTT_CLIENT_RTTHREAD_PORT_DEVICE 1
//#define MQTT_CLIENT_RTTHREAD_PORT_SOCKET 0

#ifdef MQTT_CLIENT_RTTHREAD_PORT_DEVICE
/*
* mqtt clietn port device
 */
typedef struct _mqtt_client_port_device
{
	struct _mqtt_client_port 	base;
	const char								*device_name;
	struct rt_device					*device;
	struct rt_semaphore 			rx_sem;
	rt_size_t									rx_size;
	int												oflag;
}mqtt_client_port_device_t;

int mqtt_port_device_init(mqtt_client_port_device_t *port,const char *device_name);
mqtt_client_port_device_t *mqtt_port_device_create(const char *device_name);
mqtt_client_port_device_t *mqtt_port_device_get(mqtt_client_t *client);

#endif


#ifdef MQTT_CLIENT_RTTHREAD_PORT_SOCKET
/*
* mqtt clietn port socket
 */
typedef struct _mqtt_client_port_socket
{
	struct _mqtt_client_port 	base;
	
}mqtt_client_port_socket_t;

int mqtt_port_socket_init(mqtt_client_port_socket_t *port);
mqtt_client_port_socket_t *mqtt_port_socket_create(void);
mqtt_client_port_socket_t *mqtt_port_socket_get(mqtt_client_t *client);

#endif

extern void small_mqtt_thread(void *param);
extern int small_mqtt_subscribe(mqtt_client_t *client, enum QoS qos, char *topic,mqtt_message_cb callback);
extern int small_mqtt_unsubscribe(mqtt_client_t *client, char *topic);
extern int small_mqtt_publish(mqtt_client_t *client, enum QoS qos, char *topic, void *payload, size_t length);
extern int small_mqtt_control(mqtt_client_t *client, int cmd, void *arg);
extern int small_mqtt_will(mqtt_client_t *client,unsigned char willFlag,unsigned char retained, enum QoS qos,char *topic,char *msg);
extern int small_mqtt_reconnect(mqtt_client_t *client);


extern uint32_t mqtt_tick_get(void);
extern void mqtt_delayms(int ms);
extern int mqtt_printf(mqtt_client_t *client,int level,const char *fmt, ...);

#define mqtt_debug(client,...)           mqtt_printf(client,2,__VA_ARGS__)
#define mqtt_debug_info(client,...)      mqtt_printf(client,1,__VA_ARGS__)
#define mqtt_debug_error(client,...)     mqtt_printf(client,0,__VA_ARGS__)

#define MQTT_CHECK_NET(client) \
	if(client == NULL) \
	{return MQTT_FAIL;} \
	if(client->status_net != MQTT_NET_CON) \
	{return MQTT_NET_FAIL;} 

	
#define MQTT_CHECK_TOPIC(topic) \
	if(topic == NULL) \
	{return MQTT_FAIL;}

	
#endif
